package com.weilx959.spt.app.controller;

import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTreeCell;
import com.jfoenix.controls.JFXTreeView;
import com.weilx959.spt.app.context.ConfigContext;
import com.weilx959.spt.app.context.PluginConfigurationContext;
import com.weilx959.spt.app.context.PluginContext;
import com.weilx959.spt.app.utils.ResourceBundleUtil;
import com.weilx959.spt.app.vo.SettingTreeItemVO;
import com.weilx959.spt.core.component.VDialog;
import com.weilx959.spt.core.component.VToast;
import com.weilx959.spt.core.config.Config;
import com.weilx959.spt.core.constants.CommonConstants;
import com.weilx959.spt.core.interfaces.Controller;
import com.weilx959.spt.core.interfaces.SPTPlugin;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TreeItem;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

@Slf4j
public class SettingViewController implements Controller {
    @FXML
    public AnchorPane rootPane;

    @FXML
    public JFXTreeView<SettingTreeItemVO> treeView;

    @FXML
    public ScrollPane rightScrollPane;

    @FXML
    public SplitPane mainSplitPane;

    @FXML
    public JFXButton resetButton;

    private ResourceBundle bundle;

    @Override
    public Pane getRootPane() {
        return rootPane;
    }

    @FXML
    private void initialize() {
        bundle = ResourceBundleUtil.get();
    }

    @Override
    public void onShow(Map<String, Object> config) {
        // 初始化树
        initTree();
    }

    @Override
    public boolean onClose() {
        TreeItem<SettingTreeItemVO> root = treeView.getRoot();

        if (Objects.isNull(root)) {
            return true;
        }
        boolean modified = false;

        for (TreeItem<SettingTreeItemVO> topConfigItem : root.getChildren()) {
            if (topConfigItem.getValue().getValue().equals(CommonConstants.PLUGIN_SETTING_NAME)) {
                modified = topConfigItem.getChildren()
                    .stream()
                    .anyMatch(pluginItem -> Optional.ofNullable(pluginItem)
                        .map(TreeItem::getValue)
                        .map(SettingTreeItemVO::getValue)
                        .map(PluginContext::get)
                        .map(SPTPlugin::getConfig)
                        .map(Config::modified)
                        .orElse(false));
            } else {
                modified = this.judgeModified(topConfigItem, null);
            }
            if (modified) {
                break;
            }
        }
        if (modified) {
            return VDialog.<Boolean>confirmBlocked(bundle.getString("app.setting.message.modified"), dialog -> {
                Event.fireEvent(resetButton, new ActionEvent(this, resetButton));
                dialog.close();
                return true;
            }, dialog -> {
                dialog.close();
                return false;
            });
        }
        return true;
    }

    // custom methods

    /**
     * 确认按钮事件
     *
     * @param event 事件
     */
    public void confirmAction(ActionEvent event) {
        event.consume();
        TreeItem<SettingTreeItemVO> root = treeView.getRoot();

        if (Objects.isNull(root)) {
            return;
        }
        for (TreeItem<SettingTreeItemVO> topConfigItem : root.getChildren()) {
            if (topConfigItem.getValue().getValue().equals(CommonConstants.PLUGIN_SETTING_NAME)) {
                for (TreeItem<SettingTreeItemVO> pluginItem : topConfigItem.getChildren()) {
                    savePluginConfig(PluginContext.get(pluginItem.getValue().getValue()));
                }
            } else {
                Map<String, Object> configData = new HashMap<>();
                saveAppConfig(topConfigItem, null, configData);
                log.info("New config data: {}", configData);
                ConfigContext.setConfigData(configData);
            }
        }
    }

    /**
     * 重置按钮事件
     *
     * @param event 事件
     */
    public void resetAction(ActionEvent event) {
        event.consume();
        TreeItem<SettingTreeItemVO> root = treeView.getRoot();


        if (Objects.isNull(root)) {
            return;
        }
        for (TreeItem<SettingTreeItemVO> topConfigItem : root.getChildren()) {
            if (topConfigItem.getValue().getValue().equals(CommonConstants.PLUGIN_SETTING_NAME)) {
                for (TreeItem<SettingTreeItemVO> pluginItem : topConfigItem.getChildren()) {
                    Optional.ofNullable(pluginItem)
                        .map(TreeItem::getValue)
                        .map(SettingTreeItemVO::getValue)
                        .map(PluginContext::get)
                        .map(SPTPlugin::getConfig)
                        .ifPresent(Config::reset);
                }
            } else {
                this.resetAppConfig(topConfigItem, null);
            }
        }
    }

    /**
     * 初始化树
     */
    private void initTree() {
        if (treeView.getRoot() != null) {
            return;
        }
        treeView.setCellFactory(param -> new JFXTreeCell<>() {
            @Override
            protected void updateItem(SettingTreeItemVO item, boolean empty) {
                super.updateItem(item, empty);

                if (empty || item == null) {
                    super.setText(null);
                    return;
                }
                super.setText(item.getName());
            }
        });
        TreeItem<SettingTreeItemVO> root = new TreeItem<>(SettingTreeItemVO.builder()
            .name(bundle.getString("app.setting.tree.config")).value("Config").build());
        root.setExpanded(true);
        // app配置
        this.loadAppConfig(ConfigContext.reloadConfigMap(), root, null);
        // Plugins配置
        TreeItem<SettingTreeItemVO> plugins = new TreeItem<>(SettingTreeItemVO.builder()
            .name(bundle.getString("app.setting.tree.plugins")).value(CommonConstants.PLUGIN_SETTING_NAME).build());
        plugins.setExpanded(true);
        PluginContext.getAll().forEach((id, plugin) -> {
            if (Objects.isNull(plugin.getConfig())) {
                return;
            }
            plugins.getChildren().add(new TreeItem<>(SettingTreeItemVO.builder().name(plugin.getName()).value(id).build()));
        });
        root.getChildren().add(plugins);

        treeView.setRoot(root);
        treeView.getSelectionModel().selectedItemProperty().addListener(new ConfigTreeViewChangeListener(rightScrollPane));
    }

    /**
     * 加载应用程序配置
     *
     * @param configMap  配置图
     * @param parentItem 父项
     * @param parentName 父项全名
     */
    private void loadAppConfig(Map<String, Object> configMap, TreeItem<SettingTreeItemVO> parentItem, String parentName) {
        if (MapUtils.isEmpty(configMap) || Objects.isNull(parentItem)) {
            return;
        }
        String key = StringUtils.isBlank(parentName) ? StringUtils.EMPTY : parentName + CommonConstants.Symbol.DOT;

        configMap.forEach((name, config) -> {
            String fullName = key + name;
            String treeName = bundle.getString("app.setting.tree." + fullName.toLowerCase());

            if (config instanceof Config) {
                parentItem.getChildren().add(new TreeItem<>(SettingTreeItemVO.builder().name(treeName).value(fullName).build()));
            } else if (config instanceof Map<?, ?> m) {
                TreeItem<SettingTreeItemVO> childItem = new TreeItem<>(SettingTreeItemVO.builder().name(treeName).value(fullName).build());
                parentItem.getChildren().add(childItem);
                this.loadAppConfig((Map<String, Object>)m, childItem, fullName);
            } else {
                log.error("Unsupported type of Config: {}", config);
            }
        });
    }

    /**
     * 保存插件配置
     *
     * @param plugin 插件
     */
    private void savePluginConfig(SPTPlugin plugin) {
        if (Objects.isNull(plugin)) {
            log.error("plugin is null!");
            return;
        }
        plugin.getConfig().submit()
            .failure(e -> VToast.error(e.getMessage()))
            .success(configMap -> PluginConfigurationContext.set(plugin.getID(), configMap));
    }

    /**
     * 保存应用程序配置
     *
     * @param treeItem   树项目
     * @param parentName 父级姓名
     */
    private void saveAppConfig(TreeItem<SettingTreeItemVO> treeItem, String parentName, Map<String, Object> parentConfigMap) {
        if (Objects.isNull(treeItem)) {
            return;
        }
        String parentKey = StringUtils.isBlank(parentName) ? "" : parentName + CommonConstants.Symbol.DOT;
        String key = treeItem.getValue().getValue();

        if (StringUtils.isBlank(key)) {
            return;
        }
        final String fullName  = parentKey + key;
        Config config = ConfigContext.getConfig(fullName);

        if (Objects.isNull(config)) {
            Optional.ofNullable(treeItem.getChildren())
                .ifPresent(items -> {
                    Map<String, Object> configMap = new HashMap<>();
                    parentConfigMap.put(key, configMap);
                    items.forEach(e -> saveAppConfig(e, fullName, configMap));
                });
        } else {
            config.submit()
                .map(config::deserialize)
                .failure(e -> VToast.error(e.getMessage()))
                .success(configMap -> parentConfigMap.put(key, configMap));
        }
    }

    /**
     * 遍历树，判断是否修改
     *
     * @param treeItem 树
     * @return boolean 是否修改
     */
    private boolean judgeModified(TreeItem<SettingTreeItemVO> treeItem, String parentName) {
        if (Objects.isNull(treeItem)) {
            return false;
        }
        String key = treeItem.getValue().getValue();

        if (StringUtils.isBlank(key)) {
            return false;
        }
        String fullName = StringUtils.isBlank(parentName) ? key : parentName + CommonConstants.Symbol.DOT + key;
        Config config = ConfigContext.getConfig(fullName);

        if (Objects.isNull(config)) {
            if (CollectionUtils.isEmpty(treeItem.getChildren())) {
                return false;
            }
            return treeItem.getChildren()
                .stream()
                .anyMatch(pluginItem -> judgeModified(pluginItem, fullName));
        }
        return config.modified();
    }

    /**
     * 重置应用程序配置
     *
     * @param treeItem   树项目
     * @param parentName 父级姓名
     */
    private void resetAppConfig(TreeItem<SettingTreeItemVO> treeItem, String parentName) {
        if (Objects.isNull(treeItem)) {
            return;
        }
        String key = treeItem.getValue().getValue();

        if (StringUtils.isBlank(key)) {
            return;
        }
        String fullName = StringUtils.isBlank(parentName) ? key : parentName + CommonConstants.Symbol.DOT + key;
        Config config = ConfigContext.getConfig(fullName);

        if (Objects.isNull(config)) {
            if (CollectionUtils.isEmpty(treeItem.getChildren())) {
                return;
            }
            treeItem.getChildren()
                .forEach(e -> resetAppConfig(e, fullName));
        } else {
            config.reset();
        }
    }

    /**
     * 配置树监听器
     *
     * @author weilx
     * @since 2024-05-25
     */
    private record ConfigTreeViewChangeListener(ScrollPane rightScrollPane)
        implements ChangeListener<TreeItem<SettingTreeItemVO>> {
        @Override
        public void changed(ObservableValue<? extends TreeItem<SettingTreeItemVO>> observable,
                            TreeItem<SettingTreeItemVO> oldValue, TreeItem<SettingTreeItemVO> newValue) {
            SettingTreeItemVO parentItem = Optional.ofNullable(newValue)
                .map(TreeItem::getParent)
                .map(TreeItem::getValue)
                .orElse(null);

            if (Objects.isNull(newValue) || Objects.isNull(parentItem)) { // 没有父节点的只有Config节点
                return;
            }
            if (parentItem.getValue().equals(CommonConstants.PLUGIN_SETTING_NAME)) { // 插件节点
                SPTPlugin plugin = Optional.of(newValue)
                    .map(TreeItem::getValue)
                    .map(SettingTreeItemVO::getValue)
                    .map(PluginContext::get)
                    .orElse(null);

                if (Objects.isNull(plugin) || Objects.isNull(plugin.getConfig())) {
                    rightScrollPane.setContent(null);
                } else {
                    Map<String, String> pluginFilteredMap = plugin.getConfig().layout(PluginConfigurationContext.get(plugin.getID()));
                    PluginConfigurationContext.set(plugin.getID(), pluginFilteredMap);
                    rightScrollPane.setContent(plugin.getConfig().getView());
                }
            } else { // 其他节点
                Config config = Optional.of(newValue)
                    .map(TreeItem::getValue)
                    .map(SettingTreeItemVO::getValue)
                    .map(ConfigContext::getConfig)
                    .orElse(null);

                if (Objects.isNull(config)) {
                    rightScrollPane.setContent(null);
                } else {
                    config.layout(null);
                    rightScrollPane.setContent(config.getView());
                }
            }
        }
    }
}
